package gen

import (
	"bytes"
	"fmt"
	"os"
	"regexp"
	"strings"
	"text/template"

	"gitee.com/krio/ginc/parse"
	"gitee.com/krio/ginc/util"
)

// handler handler信息
type handler struct {
	Module      string      // 项目module
	PackageName string      // 包名
	PbPath      string      // pb文件路径
	PbImport    string      // pb文件import
	PbImportAs  string      // pb文件import别名
	SvcImport   string      // service文件import
	SvcImportAs string      // service文件import别名
	Func        handlerFunc // 处理函数
}

// handlerFunc handler函数信息
type handlerFunc struct {
	Name     string // 函数名称
	Desc     string // 函数描述
	Request  string // 函数请求
	Response string // 函数返回
}

var (
	Gender          *Generator
	ginImportString = `package {{.PackageName}}

import (
	{{.PbImportAs}} "{{.PbImport}}"
	"github.com/gin-gonic/gin"
	//{{.SvcImportAs}} "{{.SvcImport}}"
)

`
	// 基于gin的func模板string
	ginFunctionString = `
// {{.Func.Name}} {{.Func.Desc}}
func {{.Func.Name}}(ctx *gin.Context, req *{{.PbImportAs}}.{{.Func.Request}}) (*{{.PbImportAs}}.{{.Func.Response}}, error) {
	//data, err := {{.SvcImportAs}}.{{.Func.Name}}(ctx, req)
	//if err != nil {
	//	return nil, err
	//}
	//return data, nil
	panic("未实现")
} `
)

// GenHandler 生产handler
func (g *Generator) GenHandler() (int, error) {
	defer func() {
		if err := recover(); err != nil {
			panic(err)
		}
	}()
	var (
		err         error
		handlerMap  = make(map[string]string) // proto内容
		newAddCount int
	)

	for _, info := range g.protoList {
		if info == nil {
			continue
		}
		if info.Service == nil || info.Service.Name == "" {
			continue
		}
		genData := &parse.ParseGenData{
			HandlerFilePath:     "",
			HandlerRelativePath: "",
			HandlerImportUrl:    "",
			HandlerImportAs:     "",
		}

		genData.HandlerFilePath = fmt.Sprintf("%s/%s", strings.Trim(g.ToHandlerPath, "/"), strings.Trim(info.Option.HandlerFile, "/"))
		pathArr := strings.Split(genData.HandlerFilePath, "/")
		pathLen := len(pathArr)
		if pathLen > 1 {
			genData.HandlerRelativePath = strings.Join(pathArr[:pathLen-1], "/")
		}
		genData.HandlerImportUrl = fmt.Sprintf("%s/%s", strings.Trim(g.goModule, "/"), genData.HandlerRelativePath)
		genData.HandlerRelativePath = strings.Trim(strings.ReplaceAll(genData.HandlerRelativePath, g.ToHandlerPath, ""), "/")
		genData.HandlerImportAs = util.GetImportAsName(genData.HandlerRelativePath, g.goModule)

		pbImportAs := util.GetImportAsName(info.Option.GoPackage, g.goModule)

		otherDirs := g.getOtherDirList(info, genData.HandlerFilePath)
		svcImportUrl := fmt.Sprintf("%s/%s", strings.Trim(g.goModule, "/"), otherDirs[0].Path)

		info.GenData = genData
		for _, api := range info.ApiList {
			var (
				exi         bool
				handlerStr  string
				handlerInfo = &handler{
					Module:      g.goModule,
					PackageName: genData.HandlerImportAs,
					PbPath:      g.FromPbPath,
					PbImport:    info.Option.GoPackage,
					PbImportAs:  pbImportAs,
					SvcImport:   svcImportUrl,
					SvcImportAs: otherDirs[0].PackageName,
					Func: handlerFunc{
						Name:     api.Name,
						Desc:     api.Desc,
						Request:  api.Request.Name,
						Response: api.Response.Name,
					},
				}
			)
			// 获取handler内容
			if handlerStr, exi = handlerMap[genData.HandlerFilePath]; !exi {
				if handlerStr, err = g.getHandlerFileBody(genData.HandlerFilePath); err != nil {
					return 0, err
				}
			}
			cs, num, err2 := g.handleFileContent(handlerInfo, handlerStr)
			if err2 != nil {
				return 0, err2
			}
			newAddCount += num
			handlerMap[genData.HandlerFilePath] = cs
		}
		for _, v := range otherDirs {
			if !util.DirExists(v.Path) { // 文件不存在
				genFilePath := fmt.Sprintf("%s/%s", v.Path, v.FileName)
				f, subErr := util.Create(genFilePath)
				if subErr != nil {
					return 0, subErr
				}
				_, err = f.WriteString(v.FileContent)
				if err != nil {
					f.Close()
					return 0, err
				}
				f.Close()
			}
		}
	}
	if err = g.writehandler(handlerMap); err != nil {
		return 0, err
	}

	return newAddCount, nil
}

// 获取handler文件内容
func (g *Generator) getHandlerFileBody(file string) (string, error) {
	if util.FileExists(file) {
		bt, err := os.ReadFile(file)
		if err != nil {
			return "", err
		}
		return string(bt), nil
	}
	return "", nil
}

func (g *Generator) handleFileContent(handlerInfo *handler, comment string) (string, int, error) {
	var (
		err error
	)
	if comment == "" { // 整个文件没有就创建文件
		val := ginImportString + ginFunctionString
		comment, err = g.handleTemplate(comment, val, handlerInfo)
		if err != nil {
			return "", 0, err
		}
		return comment, 1, nil
	} else {
		comment = handleImport(handlerInfo, comment)
		if findFunc(handlerInfo.Func, comment) {
			return comment, 0, nil
		}
		comment, err = g.handleTemplate(comment, ginFunctionString, handlerInfo)
		if err != nil {
			return "", 0, err
		}

		return comment, 1, nil
	}
}

// 处理import引用是否已经存在
func handleImport(handlerInfo *handler, val string) string {
	// pb包
	var (
		importReg  = regexp.MustCompile(handlerInfo.Module)
		findStr    = importReg.FindString(val)
		importLen  = len(findStr)
		newImports string
		pbImport   = fmt.Sprintf("%s \"%s\"", handlerInfo.PbImportAs, handlerInfo.Module)
	)
	if importLen > 0 {
		return val
	}
	importReg = regexp.MustCompile(`import \(([\s\S]+?)\)`)
	findStr = importReg.FindString(val)
	pbImport = fmt.Sprintf("%s \"%s\"", handlerInfo.PbImportAs, handlerInfo.Module)
	importLen = len(findStr)
	if importLen > 0 {
		newImports = findStr[:importLen-2] + "\n" + pbImport + "\n" + findStr[importLen-1:]
		newVal := strings.Replace(val, findStr, newImports, 1)
		return newVal
	}
	// 匹配import
	importReg = regexp.MustCompile(`import ([\s\S]+?)[^"]*"`)
	findStr = importReg.FindString(val)
	importLen = len(findStr)
	if importLen > 0 {
		newImports = strings.ReplaceAll(findStr, "import", "")
		newImports = fmt.Sprintf(`
import (
	%s
	%s
)`, newImports, pbImport)
		newVal := strings.Replace(val, findStr, newImports, 1)
		return newVal
	}
	// 匹配package
	importReg = regexp.MustCompile(`package\s+[a-zA-Z_][a-zA-Z0-9_]*`)
	findStr = importReg.FindString(val)
	newImports = fmt.Sprintf(`
%s

import (
	%s
)`, findStr, pbImport)
	newVal := strings.Replace(val, findStr, newImports, 1)
	return newVal
}

// 匹配函数是否已经存在
func findFunc(apiInfo handlerFunc, val string) bool {
	str := fmt.Sprintf(`func %s\s*\(\s*([a-zA-Z][a-zA-Z0-9]*)\s*\*gin\.Context\s*,\s*[a-zA-Z][a-zA-Z0-9_]*\s*\*\s*[a-zA-Z_][a-zA-Z0-9_.]*\.%s\s*\)`,
		apiInfo.Name,
		apiInfo.Request)
	reg, err := regexp.Compile(str)
	if err != nil {
		return false
	}
	return reg.MatchString(val)
}

// 处理template模板内容
func (g *Generator) handleTemplate(comment, tStr string, c *handler) (string, error) {
	tmpl, err := template.New("handler").Parse(tStr)
	if err != nil {
		return "", err
	}
	var buf bytes.Buffer
	if err = tmpl.Execute(&buf, c); err != nil {
		return "", err
	}

	return comment + "\n" + buf.String(), nil
}

func (g *Generator) writehandler(contents map[string]string) error {
	for file, content := range contents {
		f, err := util.Create(file)
		if err != nil {
			return err
		}
		_, err = f.WriteString(content)
		if err != nil {
			return err
		}
		err = f.Close()
		if err != nil {
			return err
		}
		if err = util.FormatGoCode(file); err != nil {
			fmt.Printf("failed to gofmt code %v, err=%v\n", file, err)
		}
	}
	return nil
}

// 通过handler的文件目录获取 service、dao、model等文件夹名称
func (g *Generator) getOtherDirList(info *parse.ParseProtoInfo, handlerFilePath string) []*OtherFileInfo {
	rootPathArr := strings.Split(strings.Trim(handlerFilePath, "/"), "/")
	pathLen := len(rootPathArr)
	if pathLen <= 2 {
		return nil
	}
	var (
		fileName = rootPathArr[pathLen-1]
		rootPath = strings.Join(rootPathArr[0:pathLen-2], "/")
		data     []*OtherFileInfo
	)
	service := g.getOtherFileInfo(rootPath, fileName, "service")
	data = append(data, service)
	entry := g.getOtherFileInfo(rootPath, fileName, "entry")
	data = append(data, entry)
	dao := g.getOtherFileInfo(rootPath, fileName, "dao")
	dao.FileContent += fmt.Sprintf(`
type %sDao struct {}
`, info.Service.Name)
	data = append(data, dao)
	repository := g.getOtherFileInfo(rootPath, fileName, "repository")
	repository.FileContent += fmt.Sprintf(`
type %sRepos interface {
}
`, info.Service.Name)
	data = append(data, repository)
	model := g.getOtherFileInfo(rootPath, fileName, "model")
	model.FileContent += fmt.Sprintf(`
const %sTableName = "xxxx"

func (*%s) TableName() string {
	return %sTableName
}

type %s struct {}`,
		info.Service.Name, info.Service.Name, info.Service.Name, info.Service.Name)
	data = append(data, model)

	return data
}

type OtherFileInfo struct {
	Path        string
	FileName    string
	FileContent string
	PackageName string
}

func (g *Generator) getOtherFileInfo(rootPath, fileName, dirName string) *OtherFileInfo {
	v := &OtherFileInfo{
		Path:     rootPath + "/" + dirName,
		FileName: fileName,
	}
	v.PackageName = util.GetImportAsName(v.Path, strings.Trim(g.ToHandlerPath, "/"))
	v.FileContent = fmt.Sprintf("package %s \n", v.PackageName)
	return v
}
